// 打开数据库。
// 在数据库中创建一个对象仓库（object store）。
// 启动一个事务，并发送一个请求来执行一些数据库操作，像增加或提取数据等。
// 通过监听正确类型的 DOM 事件以等待操作完成。
// 在操作结果上进行一些操作（可以在 request 对象中找到）


// 定义常量
const version = 1 // 版本
const dataBaseName = 'my_lib_db' // db名


function deepClone(value) {
    // 数组克隆
    if (Array.isArray(value)) {
        const clone = []
        for (var i = 0; i < value.length; i++)
            clone[i] = deepClone(value[i])
        return clone
    }
    // 对象克隆
    if (typeof value === 'object' && value !== null && value !== undefined) {
        const clone = {}
        for (const key in value)
            clone[key] = deepClone(value[key])
        return clone
    }
    return value
}

function openDB(tableSet) {
    return new Promise((resolve, reject) => {
        const request = window.indexedDB.open(dataBaseName, version);
        request.onerror = event => reject(event)
        request.onsuccess = event => resolve(event.target.result)
        // 更新数据库版本号
        request.onupgradeneeded = event => {
            createStore(event.target.result, tableSet)
        }
    })
}
/**
 * 
 * @param {*} dataBase 
 * @param {Table} table 
 */
function createStore(dataBase, tableSet) {
    for (const table of tableSet) {
        const objectStore = dataBase.createObjectStore(table.name, { keyPath: 'id', autoIncrement: true });

        // 创建单一索引
        for (const simpleIndex of table.simpleIndexSet) {
            objectStore.createIndex(simpleIndex.column, simpleIndex.column, { unique: simpleIndex.isUQ });
        }
        // 创建聚合索引
        for (const complexIndex of table.complexIndexSet) {
            objectStore.createIndex(complexIndex.name, complexIndex.columnSequence, { unique: complexIndex.isUQ });
        }
    }

}

class IndexDataBaseManager {
    tables
    data_base
    constructor(tables) {
        this.tables = tables
    }
    async mount() {
        try {
            const result = await openDB(this.tables)
            this.data_base = result
            this.data_base.onclose = event => console.log(event)

            for (const val of result.objectStoreNames)
                this[val] = new TableDataAccess(result, val)
        } catch (e) {
            console.log(e)
        }
    }
    resetDB() {
        return new Promise(resolve0 => {
            new Promise((resolve1, reject1) => {
                this.data_base.close()
                const request = window.indexedDB.deleteDatabase("my_lib_db")
                request.onsuccess = event => resolve1(event)
                request.onerror = event => reject1(event)
            }).catch(console.log).then(res => {
                console.log(res)
                this.mount().then(() => resolve0())
            })
        })
    }
}

class TableDataAccess {
    data_base
    store_name
    constructor(db, store_name) {
        this.data_base = db
        this.store_name = store_name
    }
    //简单查询
    insert(data) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readwrite")
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = store.add(deepClone(data))

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                data.id = event.target.result
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    put(data) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readwrite")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = store.put(deepClone(data))

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    get(key) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readonly")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = store.get(IDBKeyRange.only(deepClone(key)))

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    remove(key) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readwrite")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = store.delete(IDBKeyRange.only(deepClone(key)))

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    getAll() {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readonly")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = store.getAll()

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    clear() {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readwrite")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = store.clear()

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    countByIndex(indexStr, key) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readonly")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const index = store.index(indexStr)
            const request = index.count(IDBKeyRange.only(deepClone(key)))

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    count(key) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readonly")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const request = key ? store.count(IDBKeyRange.only(deepClone(key))) : store.count()
            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    /**
     * 
     * @param {String} indexStr 
     * @param {any} key 
     * @param {Boolean} isUQ 
     * @returns 
     */
    getByIndex(indexStr, key, isUQ = false) {
        return new Promise((resolve, reject) => {
            const entityTransaction = this.data_base.transaction(this.store_name, "readonly")
            entityTransaction.oncomplete = eventConsoleLog
            entityTransaction.onerror = eventConsoleLog
            const store = entityTransaction.objectStore(this.store_name)
            const index = store.index(indexStr)

            const request = isUQ ? index.get(deepClone(key)) : index.getAll(deepClone(key))

            // get错误处理
            request.onerror = event => reject(event)
            // get成功处理
            request.onsuccess = event => {
                resolve(event.target.result)
                entityTransaction.commit()
            }
        })
    }
    /**
     * 
     * @param {Set<Number>} Collection -id集合
     * @returns 
     */
    async batchGet(Collection, map = val => val) {
        const entityTransaction = this.data_base.transaction(this.store_name, 'readonly')
        entityTransaction.oncomplete = eventConsoleLog
        entityTransaction.onerror = eventConsoleLog

        const store = entityTransaction.objectStore(this.store_name)
        const result = new Set()

        const storePromise = (id) => new Promise((resolve, reject) => {
            const request = store.get(IDBKeyRange.only(deepClone(id)))
            // 成功处理
            request.onsuccess = function (event) {
                result.add(map(event.target.result))
                resolve()
            }
            // 错误处理
            request.onerror = function (event) {
                reject(event)
            }
        })

        for (const id of Collection)
            await storePromise(id)

        entityTransaction.commit()

        return result
    }
    /**
     * 
     * @param {Array<any> | Set<any>} Collection - 实体列表
     * @returns Array<Number>
     */
    async batchInsert(Collection) {
        const result = []
        if (Collection.length > 0) {
            //实体名(列表或字符串)、事务模式(readwrite,readwrite,versionchange)
            const transaction = this.data_base.transaction(this.store_name, "readwrite")
            const store = transaction.objectStore(this.store_name);
            // 事务结束处理
            transaction.oncomplete = eventConsoleLog
            // 事务错误处理
            transaction.onerror = eventConsoleLog

            const storePromise = (entity) =>
                new Promise((resolve, reject) => {
                    const request = store.add(deepClone(entity))
                    // 成功处理
                    request.onsuccess = function (event) {
                        entity.id = event.target.result
                        result.push(event.target.result)
                        resolve()
                    };
                    // 错误处理
                    request.onerror = function (event) {
                        reject(event)
                    }
                })

            for (const entity of Collection)
                await storePromise(entity)

            transaction.commit()
        }
        return result
    }
    async batchPut(Collection) {
        //实体名(列表或字符串)、事务模式(readwrite,readwrite,versionchange)
        const transaction = this.data_base.transaction(this.store_name, "readwrite")
        const store = transaction.objectStore(this.store_name);
        // 事务结束处理
        transaction.oncomplete = eventConsoleLog
        // 事务错误处理
        transaction.onerror = eventConsoleLog
        const result = []
        const storePromise = (entity) =>
            new Promise((resolve, reject) => {
                const request = store.put(deepClone(entity))
                // 成功处理
                request.onsuccess = function (event) {
                    result.push(event.target.result)
                    resolve()
                };
                // 错误处理
                request.onerror = function (event) {
                    reject(event)
                }
            })

        for (const entity of Collection)
            await storePromise(entity)
        transaction.commit()
        return result
    }
    /**
     * 
     * @param {Set<Number>} list - id列表
     * @returns 
     */
    async batchRemove(Collection) {
        //实体名(列表或字符串)、事务模式(readwrite,readwrite,versionchange)
        const transaction = this.data_base.transaction(this.store_name, "readwrite")
        const store = transaction.objectStore(this.store_name);
        // 事务结束处理
        transaction.oncomplete = eventConsoleLog
        // 事务错误处理
        transaction.onerror = eventConsoleLog
        const storePromise = (key) => {
            return new Promise((resolve, reject) => {
                const request = store.delete(deepClone(key))
                // 成功处理
                request.onsuccess = function (event) {
                    resolve(event)
                };
                // 错误处理
                request.onerror = function (event) {
                    reject(event)
                }
            })
        }

        for (const key of Collection) {
            await storePromise(key)
        }

        transaction.commit()
    }

    async _BatchGet(collection, options = { map: defaultMap, filter: defaultFilter }) {
        if (!options.map) options.map = defaultMap
        if (!options.filter) options.filter = defaultFilter

        const entityTransaction = this.data_base.transaction(this.store_name, 'readonly')
        entityTransaction.oncomplete = eventConsoleLog
        entityTransaction.onerror = eventConsoleLog
        const store = entityTransaction.objectStore(this.store_name)

        const storePromise = (id) => new Promise((resolve, reject) => {
            const request = store.get(IDBKeyRange.only(deepClone(id)))
            // 成功处理
            request.onsuccess = event => {
                if (event.target.result && options.filter(event.target.result)) {
                    resolve(options.map(event.target.result))
                } else
                    resolve()
            }
            // 错误处理
            request.onerror = event => reject(event)
        })

        try {
            const promiseResult = await Promise.all(collection.map(storePromise))
            return promiseResult.filter(val => val ? true : false)
        } catch (error) {
            console.log(error)
        } finally {
            entityTransaction.commit()
        }
    }
    async _BatchGetByIndex(indexName, params, options = { map: defaultMap, filter: defaultFilter, isUQ: false }) {
        if (!options.map) options.map = defaultMap
        if (!options.filter) options.filter = defaultFilter
        if (options.isUQ === undefined || options.isUQ === null || !(options.isUQ === true || options.isUQ === false)) options.isUQ = false

        const entityTransaction = this.data_base.transaction(this.store_name, 'readonly')
        entityTransaction.oncomplete = eventConsoleLog
        entityTransaction.onerror = eventConsoleLog
        const store = entityTransaction.objectStore(this.store_name)

        const indexPromise = (param) => {
            return new Promise((resolve, reject) => {
                const index = store.index(indexName)

                if (options.isUQ) {
                    const cursorRequest = index.get(deepClone(param))
                    cursorRequest.onerror = event => reject(event)
                    cursorRequest.onsuccess = event => {
                        if (event.target.result) {
                            if (options.filter(event.target.result))
                                resolve(options.map(event.target.result))
                        } else
                            resolve()
                    }
                } else {

                    const cursorRequest = index.getAll(deepClone(param))
                    cursorRequest.onerror = event => reject(event)
                    cursorRequest.onsuccess = event => {
                        if (event.target.result && event.target.result.length > 0) {
                            resolve(event.target.result.filter(options.filter).map(options.map))
                        } else
                            resolve(event.target.result)
                    }
                }
            })
        }

        try {
            const resultList = await Promise.all(params.map(indexPromise))
            if (options.isUQ)
                return resultList.filter(val => val ? true : false)
            else {
                const result = []
                resultList.forEach(list => {
                    list.forEach(val => {
                        if (val)
                            result.push(val)
                    })
                })
                return result
            }
        } catch (error) {
            console.log(indexName, params, options = { map: defaultMap, filter: defaultFilter, isUQ: false }, error)
        } finally {
            entityTransaction.commit()
        }
    }
}


//private
function eventConsoleLog(event) {
    console.log(event)
}
function contain(source, target) {
    const sl = source.toLowerCase()
    const tl = target.toLowerCase()
    return (source === '' && target !== '') ? false : (sl.includes(tl) || tl.includes(sl)) ? true : false
}
const defaultMap = val => val
const defaultFilter = () => true